
#include "RtBase.h"
#include "RtThreadMacEventPatch.h"
#include "RtTimerQueueOrderedList.h"


pascal void ThreadMacEventPatchTimerProc(EventLoopTimerRef inTimer, void *inUserData);
pascal void ThreadMacEventPatchTimerProc(EventLoopTimerRef inTimer, void *inUserData)
{
	CRtThreadMacEventPatch* pThreadMacEventPatch = (CRtThreadMacEventPatch*)inUserData;
	if(NULL == pThreadMacEventPatch)
		return;
	
	pThreadMacEventPatch->ProcessTimer();
	return;
}

// ------------------------------------------------------------------------
// TpMacSocketMessageHandler
// ------------------------------------------------------------------------
// our deferred call back 
pascal OSStatus
TpMacSocketMessageHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
	OSStatus			status = noErr;
	EventParamType	sanityCheck = nil;
	int *				voidRef = nil;
	
	status = GetEventParameter(inEvent,  //EventRef inEvent
							kParamNameTpSocketReference,	//EventParamName inName
							kParamTypeTpSocketReference, 	//EventParamType inDesiredType
							&sanityCheck,					//EventParamType * outActualType /* can be NULL */
  							sizeof(voidRef),				//UInt32 inBufferSize
							NULL,						//UInt32 * outActualSize /* can be NULL */
							&voidRef);					//void * outData
	if (nil != status)
		return status;

	if (kParamTypeTpSocketReference != sanityCheck)
		return 0;		// !!! should return error?
	
	CRtThreadMacEventPatch *pThreadMacEventPatch = (CRtThreadMacEventPatch *)inUserData;
	pThreadMacEventPatch->ProcessEventQueue();
	
	return noErr;
}


CRtThreadMacEventPatch::CRtThreadMacEventPatch()
	: m_pTimerQueue(NULL)
	, m_bStopped(FALSE)
{
	m_nTimer = NULL;
	m_theTimerUPP= NewEventLoopTimerUPP(ThreadMacEventPatchTimerProc);
	m_TimerFrequency = (kEventDurationMillisecond * ((EventTime)50.0));
	m_handlerUPP = NewEventHandlerUPP(TpMacSocketMessageHandler);
	m_TpEventHandlerRef = NULL;
}

CRtThreadMacEventPatch::~CRtThreadMacEventPatch()
{
	if (m_nTimer)
	{
		RemoveEventLoopTimer(m_nTimer);
		m_nTimer = NULL;
	}
	if (m_theTimerUPP)
	{
		DisposeEventLoopTimerUPP(m_theTimerUPP);
		m_theTimerUPP = NULL;
	}	
	if (m_TpEventHandlerRef)
	{
		RemoveEventHandler(m_TpEventHandlerRef);
		m_TpEventHandlerRef = NULL;
	}
	if (m_handlerUPP)
	{
		DisposeEventHandlerUPP(m_handlerUPP);
		m_handlerUPP = NULL;
	}
}

void CRtThreadMacEventPatch::OnThreadInit()
{
	Reset2CurrentThreadId();
	
	RT_ASSERTE(!m_pTimerQueue);
	m_pTimerQueue = new CRtTimerQueueOrderedList(NULL);
	
	EventTypeSpec		eventType;
	eventType.eventClass = kEventClassWebExTpEvent;
	eventType.eventKind = kEventKindWebExTpEvent;

	if (!m_TpEventHandlerRef)
	{
	InstallEventHandler(
		GetApplicationEventTarget(),		// EventTargetRef inTarget
			m_handlerUPP,						// EventHandlerUPP inHandler
		1,								// UInt32 inNumTypes
		&eventType,						// const EventTypeSpec * inList
		this,								// void *                 inUserData,
		  	&m_TpEventHandlerRef);							// EventHandlerRef * outRef	
	}
	
	if(!m_nTimer)
	{  	
	InstallEventLoopTimer(	GetCurrentEventLoop(),
							m_TimerFrequency,
							m_TimerFrequency,
							m_theTimerUPP,
							this,
							&m_nTimer);  	
	}							
}

void CRtThreadMacEventPatch::OnThreadRun()
{
	RunApplicationEventLoop();
}

RtResult CRtThreadMacEventPatch::Stop(CRtTimeValue* aTimeout)
{
	if (m_nTimer)
	{
		RemoveEventLoopTimer(m_nTimer);
		m_nTimer = NULL;
	}
	// todo, let RunApplicationEventLoop() return.
	QuitApplicationEventLoop();
	
	return RT_OK;
}

IRtEventQueue* CRtThreadMacEventPatch::GetEventQueue()
{
	return this;
}

IRtTimerQueue* CRtThreadMacEventPatch::GetTimerQueue()
{
	return m_pTimerQueue;
}

RtResult CRtThreadMacEventPatch::PostEvent(IRtEvent *aEvent, EPriority aPri)
{
	RtResult rv = CRtEventQueueUsingMutex::PostEvent(aEvent, aPri);
	if (RT_FAILED(rv))
		return rv;
		
	// notify main thread.
	TpMacSocketMessage();
	
	return RT_OK;
}

RtResult CRtThreadMacEventPatch::ProcessEventQueue()
{
	DWORD dwRemainSize = 0;
	CRtEventQueueBase::EventsType listEvents;
	RtResult rv = CRtEventQueueUsingMutex::PopPendingEventsWithoutWait(
		listEvents, CRtEventQueueBase::MAX_GET_ONCE, &dwRemainSize);
		
	
	if (RT_SUCCEEDED(rv))
		rv = CRtEventQueueUsingMutex::ProcessEvents(listEvents);
	if (dwRemainSize)
		TpMacSocketMessage();
	return rv;
}

RtResult CRtThreadMacEventPatch::ProcessTimer()
{
	if (m_pTimerQueue)
		m_pTimerQueue->CheckExpire();
	return RT_OK;
}


// ------------------------------------------------------------------------
// TpMacSocketMessage
// ------------------------------------------------------------------------
//
void
CRtThreadMacEventPatch::TpMacSocketMessage()
{
	EventRef theTpSocketEvent = NULL;

	try
	{
		OSStatus err;

		err = CreateEvent(NULL, 						//CFAllocatorRef inAllocator
						kEventClassWebExTpEvent,		//UInt32 inClassID
						kEventKindWebExTpEvent,	//UInt32 kind
						(EventTime)0.0,				//EventTime when
						kEventAttributeUserEvent,		//EventAttributes flags
						&theTpSocketEvent);	//EventRef* outEvent

	
		err = SetEventParameter(theTpSocketEvent, 	// EventRef inEvent
							kParamNameTpSocketReference,	// EventParamName   inName
							kParamTypeTpSocketReference,	// EventParamType inType
							sizeof(void*),				// UInt32 inSize
							this);						// const void * inDataPtr
		
		err = PostEventToQueue(GetMainEventQueue(),
							theTpSocketEvent,
	  						kEventPriorityLow);

	}
	catch(...)
	{
		if (NULL != theTpSocketEvent)
			ReleaseEvent(theTpSocketEvent);
	}
}


